import { LogicDriver } from "../etc/LogicDriver";
import GameModel from "../model/GameModel";
import {
    AbstractMotion,
    ExtendsMotion,
    MotionContext,
    RowMotion,
    ServerMotion
} from "./MotionBase";
import { Motion, MotionID, MotionLibrary } from "./MotionLibrary";

type MotionPerformantExtendsConstructor = { new():ExtendsMotion }
type MotionNumericalExtendsConstructor = { new():ServerMotion }
export class MotionRegister {
    static extendsMapClient:Map<string,MotionPerformantExtendsConstructor> = new Map()
    static extendsMapServer:Map<string,MotionNumericalExtendsConstructor> = new Map()

    static registerClientExtends(name:string, motion:MotionPerformantExtendsConstructor){
        if(this.extendsMapClient.has(name)){
            console.error(`添加了重复的motion构造函数:${name}`)
            return
        }
        this.extendsMapClient.set(name,motion)
    }

    static registerServerExtends(name:string, motion:MotionNumericalExtendsConstructor){
        if(this.extendsMapServer.has(name)){
            console.error(`添加了重复的motion构造函数:${name}`)
            return
        }
        this.extendsMapServer.set(name,motion)
    }

    static getPerformantMotionInstance(name:string):AbstractMotion{
        if(this.extendsMapClient.has(name)){
            let ctor = this.extendsMapClient.get(name)
            return new ctor()
        }
        let rowMotionData = MotionLibrary[name] as Motion
        if(!rowMotionData){
            console.error(`未定义此Motion, name:${name}`)
            throw new Error(`未定义此Motion, name:${name}`)
        }

        return new RowMotion(rowMotionData)
    }

    static getNumericalMotionInstance(name:string):ServerMotion{
        if(this.extendsMapServer.has(name)){
            let ctor = this.extendsMapServer.get(name)
            return new ctor()
        }
        let rowMotionData = MotionLibrary[name] as Motion
        if(!rowMotionData){
            console.error(`未定义此Motion, name:${name}`)
            return null
        }

        return new ServerMotion(rowMotionData)
    }
}

class MotionProcess {
    private readonly stageMotion: Set<AbstractMotion>
    private readonly motionPool: Map<string, Set<AbstractMotion>>

    private motionMDC: MotionModuleC

    constructor() {
        this.motionPool = new Map()
        this.stageMotion = new Set()

        if (mw.SystemUtil.isClient()) {
            this.motionMDC = ModuleService.getModule(MotionModuleC)
        }
    }

    invokeMotion(motionID: MotionID, context: MotionContext, performer: Character) {
        // 如果需要覆盖则回收正在执行的motion
        for (let motion of this.stageMotion) {
            if (context.from != motion.motionContext.from) continue
            this.recycleMotion(motion)
            break
        }

        let motion = this.getMotionFromPool(motionID)
        motion.motionContext = context
        motion.currentFrame = 0
        motion.stage = true

        motion.invoke(performer)

        if (motion.isOwner) this.setOwnerState(motion)
    }

    abortMotion(ownerID: number) {
        let targetMotion: AbstractMotion
        for (let motion of this.stageMotion.values()) {
            if (motion.motionContext.from == ownerID) {
                targetMotion = motion
                break
            }
        }
        if (!targetMotion) return
        this.recycleMotion(targetMotion)
    }

    updateLogic() {
        for (let motion of this.stageMotion) {
            try { motion.updateLogic() }
            catch (e) { console.error(`Motion/${motion.constructor.name}/updateLogic  出现错误!,错误信息:${e.message}\n${e.stack}`) }
            finally { motion.currentFrame++ }

            // 回收
            if (motion.currentFrame >= motion.motionData.count) {
                this.recycleMotion(motion)
            }
        }
    }
    updatePerformant() { for (let action of this.stageMotion) { action.updatePerformant() } }


    getMotionFromPool(motionID: MotionID): AbstractMotion {
        // 没有此池子则创建pool TODO 定时清空
        if (!this.motionPool.has(motionID)) {
            let abs = MotionRegister.getPerformantMotionInstance(motionID)
            this.stageMotion.add(abs)
            return abs
        }

        // 在池中查找未激活的返回
        let motion: AbstractMotion
        let collection = this.motionPool.get(motionID)
        for (let value of collection.values()) {
            if (value.stage == false) {
                motion = value
                this.stageMotion.add(motion)
                collection.delete(motion)
                break;
            }
        }

        // 加入在池内创建
        if (!motion) {
            let abm = MotionRegister.getPerformantMotionInstance(motionID)
            motion = abm
            this.stageMotion.add(abm)
        }

        return motion
    }

    recycleMotion(motion: AbstractMotion) {
        motion.finish()

        if (motion.isOwner) this.setOwnerState(undefined)

        motion.stage = false
        // 从stage删掉
        this.stageMotion.delete(motion)
        // 没有则创建容器
        if (!this.motionPool.has(motion.constructor.name)) {
            this.motionPool.set(motion.constructor.name, new Set())
        }
        // 添加到容器
        this.motionPool.get(motion.constructor.name).add(motion)
    }

    // 赋值帧状态
    private setOwnerState(motion: AbstractMotion) {
        this.motionMDC.setCurrentMotion(motion)
    }
}

export class MotionModuleC extends ModuleC<MotionModuleS, null>{
    static PlayMotionMessageHandle = '__motion'
    private process: MotionProcess

    private _currentMotion: AbstractMotion | undefined
    get currentMotion(): AbstractMotion | undefined { return this._currentMotion }

    async onStart() {
        this.process = new MotionProcess()
        LogicDriver.bindLogicLoop(this.process.updateLogic.bind(this.process))
        Event.addServerListener(MotionModuleC.PlayMotionMessageHandle, this.onCharacterInvokeMotion.bind(this))
    }

    private onCharacterInvokeMotion(characterGUID: string, motionID: MotionID) {
        let performer = <Character>GameObject.findGameObjectById(characterGUID)
        if (!performer) return
        this.process.invokeMotion(motionID, { from: -99999 }, performer)
    }

    onUpdate() {
        this.process.updatePerformant()
    }

    // 设置玩家当前执行的motion
    setCurrentMotion(motion?: AbstractMotion) {
        this._currentMotion = motion
    }

    // 玩家发起motion
    invokeMotion(motionName: MotionID) {
        // 报告给服务器
        this.server.net_player_invoke_motion(motionName)
    }

    // 房间内其他玩家发起motion
    net_room_player_invoke_motion(motionID: MotionID, context: MotionContext) {
        let character = GameModel.instance.getCharacterBySceneID(context.from)
        // 没找到motion发起方
        if (!character) return

        this.process.invokeMotion(motionID, context, character)
    }

    // 由服务器发送的终止怪物的motion
    net_server_abort_motion(sceneID: number) {
        this.process.abortMotion(sceneID)
    }

    invokeMotionOnlyClient(motionName: MotionID, targetPlayerId: number, performer: Character) {
        // 客户端预表现
        this.process.invokeMotion(motionName, {
            from: targetPlayerId,
            to: undefined
        }, performer)
    }

    abortMotion(ownerID: number) {
        this.process.abortMotion(ownerID)
    }
}


export class MotionModuleS extends ModuleS<MotionModuleC, null>{
    private stageMotion:Set<ServerMotion> = new Set()
    private motionPool:Map<string,Set<ServerMotion>> = new Map()

    protected onStart(): void {
        LogicDriver.bindLogicLoop(this.logicUpdate.bind(this))
    }

    private logicUpdate(){
        for (let motion of this.stageMotion) {
            try{ motion.updateLogic() }
            catch (e) { console.error(`Server Motion/${motion.constructor.name}/updateLogic 出现错误!,错误信息:${e.message}\n${e.stack}`) }
            finally { motion.currentFrame ++ }
            if(motion.currentFrame >= motion.motionData.count) {
                this.recycleMotion(motion)
            }
        }
    }

    // 获取一个Motion
    private getMotionFromPool(type:string):ServerMotion{
        // 没有此池子则创建pool
        if(!this.motionPool.has(type)){
            let abs = MotionRegister.getNumericalMotionInstance(type)
            this.stageMotion.add(abs)
            return abs
        }
        let motion:ServerMotion
        let collection = this.motionPool.get(type)
        for (let value of collection.values()) {
            if(value.stage == false){
                motion = value
                this.stageMotion.add(motion)
                collection.delete(motion)
                break;
            }
        }
        if(!motion){
            let abs = MotionRegister.getNumericalMotionInstance(type)
            motion = abs
            this.stageMotion.add(abs)
        }
        return motion
    }

    // 回收Motion
    private recycleMotion(motion:ServerMotion){
        motion.finish()

        motion.stage = false
        // 从stage删掉
        this.stageMotion.delete(motion)
        // 没有则创建容器
        if(!this.motionPool.has(motion.constructor.name)){
            this.motionPool.set(motion.constructor.name,new Set())
        }
        // 添加到容器
        this.motionPool.get(motion.constructor.name).add(motion)
    }

    // 服务器Motion
    private invokeServerMotion(motionName:MotionID, from:number){
        // 如果当前角色正在播放motion则停止
        let currentMotion = this.getMotionByPlayerID(from)
        if(currentMotion) this.recycleMotion(currentMotion)

        let motion = this.getMotionFromPool(motionName)
        motion.currentFrame = 0
        motion.stage = true
        motion.invoke(from)
    }

    // 根据玩家ID获取motion
    getMotionByPlayerID(playerID:number):ServerMotion|undefined{
        for (const motion of this.stageMotion) {
            if(motion.ownerCharacterSceneID == playerID){
                return motion
            }
        }
        return undefined
    }

    // 同步 Motion
    syncMotion(motionName:MotionID | string, context:MotionContext){
        this.getAllClient().net_room_player_invoke_motion(motionName as MotionID,context)
    }

    // 中断motion
    abortMotion(sceneID:number){
        this.getAllClient().net_server_abort_motion(sceneID)
    }

    // 服务器主动派发motion
    dispatchMotionToAllClient(motionName:string, playerID:number){
        let context = new MotionContext(playerID, undefined)
        let motionID = motionName as MotionID
        // 转发给所有客户端
        this.syncMotion(motionID,context)
        // 服务器跑motion
        this.invokeServerMotion(motionID, context.from)
    }

    //客户端请求转发Motion
    net_player_invoke_motion(motionName:string){
        let context = new MotionContext(this.currentPlayerId,undefined)
        let motionID = motionName as MotionID
        // 转发给所有客户端
        this.syncMotion(motionID,context)
        // 服务器跑motion(只有玩家跑)
        this.invokeServerMotion(motionID, context.from)
    }
}